package com.mingqijia.gassafety.authority.dal.provider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mingqijia.gassafety.authority.api.resp.config.DictionaryItemRespDTO;
import com.mingqijia.gassafety.authority.api.resp.department.DepartmentRespDTO;
import com.mingqijia.gassafety.authority.dal.command.CreateDepartmentCommand;
import com.mingqijia.gassafety.authority.dal.command.QueryDepartmentCommand;
import com.mingqijia.gassafety.authority.dal.command.RemoveDepartmentCommand;
import com.mingqijia.gassafety.authority.dal.command.UpdateDepartmentCommand;
import com.mingqijia.gassafety.authority.dal.handler.HierarchyTranslator;
import com.mingqijia.gassafety.authority.dal.helper.ConfigureHolder;
import com.mingqijia.gassafety.db.entity.auth.CompanyEntry;
import com.mingqijia.gassafety.db.entity.auth.HierarchyEntry;
import com.mingqijia.gassafety.db.mapper.auth.HierarchyMapper;
import com.mingqijia.gassafety.shared.constant.Constants;
import com.mingqijia.gassafety.shared.cqrs.QueryHandler;
import com.mingqijia.gassafety.shared.enums.RunningState;
import com.mingqijia.gassafety.shared.enums.YesNoEnum;
import com.mingqijia.gassafety.shared.utils.ApplicationUtils;
import com.mingqijia.gassafety.shared.utils.MappingTools;
import com.mingqijia.gassafety.shared.handler.GlobalAuthenticationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 信息CU操作(C-create,U-update)
 * @author Damon S.
 * @version v0.2
 * @date 2021-06-19
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class HierarchyProvider extends ServiceImpl<HierarchyMapper, HierarchyEntry> implements QueryHandler<HierarchyEntry> {

    private final ConfigureHolder holder;
    private final HierarchyTranslator translator;
    private final CompanyProvider provider;

    public List<DepartmentRespDTO> findHierarchy(String usage) {
        QueryWrapper<HierarchyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(HierarchyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(HierarchyEntry::getState, RunningState.RUNNING.getCode());

        // 只返回部门,且不带上级公司
        if (Objects.equals(usage, Constants.DEPARTMENT)) {
            queryClause.lambda().eq(HierarchyEntry::getType, Constants.DEPARTMENT);
        }

        List<HierarchyEntry> entries = this.list(queryClause);
        if (CollectionUtils.isEmpty(entries)) {
            return Collections.emptyList();
        }
        List<DepartmentRespDTO> rawDepartments = translator.toRespDTO(entries);

        // 提取根部门
        List<DepartmentRespDTO> roots = rawDepartments.stream().filter(
                resp -> Objects.equals(
                        MappingTools.toRaw(resp.getParentId()),
                        ApplicationUtils.getHeaderSpId()
                )
        ).collect(Collectors.toList());
        // 构建部门树
        for (DepartmentRespDTO department : roots) {
            buildDepartmentHierarchy(rawDepartments, department);
        }
        return roots;
    }

    public Page<HierarchyEntry> find(QueryDepartmentCommand command) {
        LambdaQueryWrapper<HierarchyEntry> queryClause = new LambdaQueryWrapper<>();
        Optional.ofNullable(command.getName()).ifPresent(
                name -> queryClause.like(HierarchyEntry::getName, name)
        );
        Optional.ofNullable(command.getType()).ifPresent(
                type -> queryClause.eq(HierarchyEntry::getType, type)
        );
        if (Objects.isNull(command.getState()) || RunningState.NA.equals(command.getState())) {
            queryClause.eq(HierarchyEntry::getState, RunningState.RUNNING.getCode());
        } else {
            queryClause.eq(HierarchyEntry::getState, command.getState().getCode());
        }
        queryClause.eq(HierarchyEntry::getIsDeleted, YesNoEnum.N.getCode())
                .eq(HierarchyEntry::getSpId, ApplicationUtils.getHeaderSpId());
        return this.page(createPage(command), queryClause.orderByAsc(HierarchyEntry::getOrdinal));
    }

    public void buildDisplay(List<DepartmentRespDTO> departments) {
        Map<String, DictionaryItemRespDTO> types =
                holder.getDictionaryInMap(Constants.CACHE_KEY_ORG_TYPE_ENT);

        departments.forEach(department -> {
            department.setTypeCn(types.get(department.getType()).getName());
            if (CollectionUtils.isEmpty(department.getParentId())) {
                return;
            }
            // 构建父级名称
            List<HierarchyEntry> entries = listByIds(department.getParentId());

            Map<String, HierarchyEntry> map = new HashMap<>();
            entries.forEach(entry -> map.put(entry.getId(), entry));

            CompanyEntry company = provider.getById(
                    department.getParentId().get(Constants.INT_ZERO)
            );

            StringBuilder builder = new StringBuilder();
            if (Objects.nonNull(company)) {
                builder.append(Constants.STR_STRIKETHROUGH).append(company.getName());
            }
            HierarchyEntry entry;
            for (String parentId : department.getParentId()) {
                entry = map.get(parentId);
                if (Objects.isNull(entry)) {
                    continue;
                }
                builder.append(Constants.STR_STRIKETHROUGH).append(entry.getName());
            }
            department.setParentCn(builder.substring(Constants.INT_ONE));
        });
    }

    @Transactional(rollbackFor = Exception.class)
    public String create(CreateDepartmentCommand command) {
        this.attachOperator2Command(command);

        if (Objects.isNull(command.getState()) ||
                RunningState.NA.equals(command.getState())) {
            command.setState(RunningState.RUNNING);
        }
        if (ObjectUtils.isEmpty(command.getOrdinal())) {
            command.setOrdinal(Constants.INT_ZERO);
        }
        this.checkHierarchy(command.getParentId(), command.getType());

        QueryWrapper<HierarchyEntry> queryClause = new QueryWrapper<>();
        queryClause.lambda().eq(HierarchyEntry::getName, command.getName())
                .eq(HierarchyEntry::getState, YesNoEnum.N.getCode())
                .eq(HierarchyEntry::getParentId, command.getParentId());
        if (!Objects.equals(Constants.INT_ZERO, this.count(queryClause))) {
            throw new GlobalAuthenticationException("该组织已存在[" + command.getName() + "]");
        }

        HierarchyEntry entry = translator.toEntry(command);
        if (!this.save(entry)) {
            throw new GlobalAuthenticationException("创建组织失败");
        }
        return entry.getId();
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean update(UpdateDepartmentCommand command) {
        this.attachOperator2Command(command);
        this.checkHierarchy(command.getParentId(), command.getType());

        UpdateWrapper<HierarchyEntry> updateClause = new UpdateWrapper<>();
        updateClause.lambda().eq(HierarchyEntry::getId, command.getOrgId());

        HierarchyEntry entry = translator.toEntry(command);
        return this.update(entry, updateClause);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean remove(RemoveDepartmentCommand command) {
        this.attachOperator2Command(command);

        LambdaUpdateWrapper<HierarchyEntry> updateClause = new LambdaUpdateWrapper<>();
        updateClause.in(HierarchyEntry::getId, command.getOrgIds())
                .set(HierarchyEntry::getUpdatedBy, command.getUpdatedBy())
                .set(HierarchyEntry::getIsDeleted, YesNoEnum.Y.getCode());

        return this.update(updateClause);
    }

    private void checkHierarchy(List<String> parentId, String type) {
        if (CollectionUtils.isEmpty(parentId)) {
            return;
        }
        HierarchyEntry entry = getById(
                parentId.get(parentId.size() - Constants.INT_ONE)
        );
        if (Objects.isNull(entry)) {
            return;
        }
        switch (entry.getType()) {
            case Constants.TEAM :
                if (Objects.equals(type, Constants.DEPARTMENT)) {
                    throw new GlobalAuthenticationException("班组下不可创建部门");
                }
                break;
            case Constants.POSITION :
                throw new GlobalAuthenticationException("职位下不可创建组织");
            case Constants.DEPARTMENT :
            default:
                break;
        }
    }

    private void buildDepartmentHierarchy(List<DepartmentRespDTO> sources,
                                        DepartmentRespDTO target) {
        target.setChildren(sources.stream().filter(item -> {
            if (CollectionUtils.isEmpty(item.getParentId())) {
                return false;
            }
            return Objects.requireNonNull(
                    MappingTools.toRaw(item.getParentId())
            ).endsWith(target.getId());
        }).sorted(Comparator.comparing(DepartmentRespDTO::getOrdinal))
                .collect(Collectors.toList()));
        if (CollectionUtils.isEmpty(target.getChildren())) {
            return;
        }
        for (DepartmentRespDTO resp : target.getChildren()) {
            this.buildDepartmentHierarchy(sources, resp);
        }
    }
}
